#include <crt_util.h>

#if (TARGET_OS == OS_WIN)
#define CTL_PluginExtName _T(".dll")
#elif (TARGET_OS == OS_POSIX)
#define CTL_PluginExtName _T(".so")
#elif (TARGET_OS == OS_MACH)

#ifdef __APPLE__
#if _MACOSX_XCODE_
#if (TARGET_OS_IOS == 1) && (TARGET_OS_IPHONE == 1)
#define CTL_PluginExtName _T(".framework")
#elif (TARGET_OS_MAC == 1)
#define CTL_PluginExtName _T(".framework")
#endif
#else
#define CTL_PluginExtName _T(".dylib")
#endif
#else

#endif

#elif (TARGET_OS == OS_UNIX)
#define CTL_PluginExtName _T(".so")
#endif

int fileisexist(const basic_tchar* file)
{
    if (NULL == file || 0x00 == (*file))
        return S_ERROR;

#if (TARGET_OS == OS_WIN)

    DWORD dwAttributes = GetFileAttributes(file);
    if (0xFFFFFFFF == dwAttributes || dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
        return S_ERROR;

#elif (TARGET_OS == OS_POSIX)

    int fd = access(file, F_OK);
    if (fd != F_OK)
        return S_ERROR;

#elif (TARGET_OS == OS_MACH)

    int fd = access(file, F_OK);
    if (fd != F_OK)
        return S_ERROR;

#elif (TARGET_OS == OS_UNIX)

    int fd = access(file, F_OK);
    if (fd != F_OK)
        return S_ERROR;

#endif
    return S_SUCCESS;
}
int fileisdirectory(const basic_tchar* dir)
{
    int rc = S_ERROR;
    rc_error(dir != NULL, S_ERROR);
#if (TARGET_OS == OS_WIN)

    DWORD dwAttributes = GetFileAttributes(dir);
    if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
        rc = S_SUCCESS;

#elif (TARGET_OS == OS_POSIX)

    struct stat st;
    int res = stat(dir, &st);
    if (res == 0 && S_ISDIR(st.st_mode))
        rc = S_SUCCESS;

#elif (TARGET_OS == OS_MACH)

    struct stat st;
    int res = stat(dir, &st);
    if (res == 0 && S_ISDIR(st.st_mode))
        rc = S_SUCCESS;

#elif (TARGET_OS == OS_UNIX)

    struct stat st;
    int res = stat(dir, &st);
    if (res == 0 && S_ISDIR(st.st_mode))
        rc = S_SUCCESS;

#endif

    return rc;
}

int get_dynamicpath(void* name, basic_tchar* path, size_t len)
{
#if (TARGET_OS == OS_WIN)
    GetModuleFileNameW(GetModuleHandle((basic_tchar*)name), path, len);
#elif (TARGET_OS == OS_POSIX)

    Dl_info info = {0x00};
    int rc = 0;
    rc = dladdr((void*)name, &info);
    string_strcpy(path, len, info.dli_fname);
    return rc == 0 ? S_SUCCESS : S_ERROR;

#elif (TARGET_OS == OS_MACH)

    Dl_info info = {0x00};
    int rc = 0;
    rc = dladdr((void*)name, &info);
    string_strcpy(path, len, info.dli_fname);
    return rc == 0 ? S_SUCCESS : S_ERROR;

#elif (TARGET_OS == OS_UNIX)

    Dl_info info = {0x00};
    int rc = 0;
    rc = dladdr((void*)name, &info);
    string_strcpy(path, len, info.dli_fname);
    return rc == 0 ? S_SUCCESS : S_ERROR;

#endif
    return S_SUCCESS;
}

int get_executepath(_pid_t pid, basic_tchar* path, pathlen len)
{
    basic_tchar buf[MAX_PATH + 1] = {0x00};

#if (TARGET_OS == OS_WIN)
    GetModuleFileNameW(pid_instance, buf, MAX_PATH);
    PathRemoveFileSpecW(buf);
    s_wstrcpy(path, len, buf);
    return S_SUCCESS;
#elif (TARGET_OS == OS_POSIX)

    int count = 0;
    char* path_end = NULL;
    pid_t cur_pid = getpid();
    char cur_exe_link[MAX_PATH] = {0};
    sprintf(cur_exe_link, "/proc/%u/exe", cur_pid);
    count = readlink(cur_exe_link, buf, MAX_PATH + 1);
    if (count < 0 || count >= MAX_PATH)
        return FALSE;

    int iloop;
    buf[count] = '\0';
    for (iloop = count; iloop >= 0; iloop--) {
        if (buf[iloop] == '/') {
            buf[iloop + 1] = '\0';
            break;
        }
    }

    s_strcpy(path, len, buf);
    return S_SUCCESS;

#elif (TARGET_OS == OS_MACH)

    int count = 0;
    char* p = NULL;
    pathlen size = len;
    count = _NSGetExecutablePath(buf, &size);

    p = s_strrchr(buf, '/');
    if (p != NULL) {
        *p = 0;
    }
    s_strcpy(path, count, buf);
    return S_SUCCESS;

#elif (TARGET_OS == OS_UNIX)

    int count = 0;
    char* path_end = NULL;
    pid_t cur_pid = getpid();
    char cur_exe_link[MAX_PATH] = {0};
    sprintf(cur_exe_link, "/proc/%u/exe", cur_pid);
    count = readlink(cur_exe_link, buf, MAX_PATH + 1);
    if (count < 0 || count >= MAX_PATH)
        return FALSE;

    int iloop;
    buf[count] = '\0';
    for (iloop = count; iloop >= 0; iloop--) {
        if (buf[iloop] == '/') {
            buf[iloop + 1] = '\0';
            break;
        }
    }

    s_strcpy(path, len, buf);
    return S_SUCCESS;

#endif
    return S_SUCCESS;
}

int get_dynamicname(const basic_tchar* name, basic_tchar* pluginame, size_t len)
{
#if (TARGET_OS == OS_WIN)
    s_wstrcat(pluginame, len, name);
#elif (TARGET_OS == OS_POSIX)
    s_strcat(pluginame, len, _T("lib"));
    s_strcat(pluginame, len, name);
#elif (TARGET_OS == OS_MACH)

#ifdef __APPLE__
#if _MACOSX_XCODE_
#if (TARGET_OS_IOS == 1) && (TARGET_OS_IPHONE == 1)
    s_strcat(pluginame, len, name);
    s_strcat(pluginame, len, CTL_PluginExtName);
    s_strcat(pluginame, len, "/");
    s_strcat(pluginame, len, name);
    return S_SUCCESS;
#elif (TARGET_OS_MAC == 1)
    s_strcat(pluginame, len, name);
    s_strcat(pluginame, len, CTL_PluginExtName);
    s_strcat(pluginame, len, "/");
    s_strcat(pluginame, len, name);
    return S_SUCCESS;
#endif
#else
    s_strcat(pluginame, len, _T("lib"));
    s_strcat(pluginame, len, name);
#endif
#else

#endif

#elif (TARGET_OS == OS_UNIX)
    s_strcat(pluginame, len, _T("lib"));
    s_strcat(pluginame, len, name);

#endif
    tstring_strcat(pluginame, len, CTL_PluginExtName);

    return S_SUCCESS;
}

#if (TARGET_OS == OS_WIN)

int get_file_info(basic_tchar* cFilePath, get_file_cb entry, void* ctx)
{
    /*
    If you are writing a 32 - bit application to list all the files in a directory and the
    application may be run on a 64 - bit computer, you should call the
    Wow64DisableWow64FsRedirection function before calling FindFirstFile and call
    Wow64RevertWow64FsRedirection after the last call to FindNextFile. For more information, see
    File System Redirector.
    */
    WIN32_FIND_DATA data;
    HANDLE hFind = INVALID_HANDLE_VALUE;
    WCHAR cFullPath[MAX_PATH + 1] = {0x00};
    WCHAR cNewPath[MAX_PATH + 1] = {0x00};
    s_memset(&data, 0, sizeof(WIN32_FIND_DATA));

    swprintf_s(cFullPath, MAX_PATH, L"%s\\*.*", cFilePath);
    hFind = FindFirstFile(cFullPath, &data);

    if (hFind == INVALID_HANDLE_VALUE)
        return S_SUCCESS;

    do {
        if ((!s_wstricmp(L".", data.cFileName)) || (!s_wstricmp(L"..", data.cFileName))) {
            continue;
        }
        if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            swprintf_s(cNewPath, MAX_PATH, L"%s\\%s", cFilePath, data.cFileName);
            get_file_info(cNewPath, entry, ctx);
        } else {
            if (data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
            } else if (data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
            } else {
                swprintf_s(cFullPath, MAX_PATH, L"%s\\%s", cFilePath, data.cFileName);
                entry(cFullPath, ctx);
            }
        }

    } while (FindNextFile(hFind, &data));

    if (hFind != INVALID_HANDLE_VALUE) {
        FindClose(hFind);
        hFind = NULL;
    }

    return S_SUCCESS;
}

#elif (TARGET_OS == OS_POSIX)

#include <dirent.h>
#include <sys/stat.h>

#define FILE_MAX_PATH 512

void dirwalk(basic_tchar* dir, int (*func)(basic_tchar* name, get_file_cb entry, void* ctx), get_file_cb entry, void* ctx)
{
    char name[FILE_MAX_PATH] = {0x00};
    struct dirent* dp = NULL;
    DIR* dfd = NULL;

    if ((dfd = opendir(dir)) == NULL) {
        fprintf(stderr, "dirwalk: can't open %s\n", dir);
        return;
    }

    while ((dp = readdir(dfd)) != NULL) {
        if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
            continue;
        }

        if (strlen(dir) + strlen(dp->d_name) + 2 > sizeof(name)) {
            fprintf(stderr, "dirwalk : name %s %s too long\n", dir, dp->d_name);
        } else {
            sprintf(name, "%s/%s", dir, dp->d_name);
            (*func)(name, entry, ctx);
        }
    }
    closedir(dfd);
}

int get_file_info(basic_tchar* name, get_file_cb entry, void* ctx)
{
    struct stat stbuf;

    if (stat(name, &stbuf) == -1) {
        fprintf(stderr, "file size: open %s failed\n", name);
        return S_ERROR;
    }

    if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
        dirwalk(name, get_file_info, entry, ctx);
    } else {
        // printf("%8ld %s\n", stbuf.st_size, name);
        entry(name, ctx);
    }
    return S_SUCCESS;
}
#elif (TARGET_OS == OS_MACH)

#include <dirent.h>
#include <sys/stat.h>

#define FILE_MAX_PATH 512

void dirwalk(basic_tchar* dir, int (*func)(basic_tchar* name, get_file_cb entry, void* ctx), get_file_cb entry, void* ctx)
{
    char name[FILE_MAX_PATH] = {0x00};
    struct dirent* dp = NULL;
    DIR* dfd = NULL;

    if ((dfd = opendir(dir)) == NULL) {
        fprintf(stderr, "dirwalk: can't open %s\n", dir);
        return;
    }

    while ((dp = readdir(dfd)) != NULL) {
        if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
            continue;
        }

        if (strlen(dir) + strlen(dp->d_name) + 2 > sizeof(name)) {
            fprintf(stderr, "dirwalk : name %s %s too long\n", dir, dp->d_name);
        } else {
            sprintf(name, "%s/%s", dir, dp->d_name);
            (*func)(name, entry, ctx);
        }
    }
    closedir(dfd);
}

int get_file_info(basic_tchar* name, get_file_cb entry, void* ctx)
{
    struct stat stbuf;

    if (stat(name, &stbuf) == -1) {
        fprintf(stderr, "file size: open %s failed\n", name);
        return S_ERROR;
    }

    if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
        dirwalk(name, get_file_info, entry, ctx);
    } else {
        // printf("%8ld %s\n", stbuf.st_size, name);
        entry(name, ctx);
    }
    return S_SUCCESS;
}

#elif (TARGET_OS == OS_UNIX)

#include <dirent.h>
#include <sys/stat.h>

#define FILE_MAX_PATH 512

void dirwalk(basic_tchar* dir, int (*func)(basic_tchar* name, get_file_cb entry, void* ctx), get_file_cb entry, void* ctx)
{
    char name[FILE_MAX_PATH] = {0x00};
    struct dirent* dp = NULL;
    DIR* dfd = NULL;

    if ((dfd = opendir(dir)) == NULL) {
        fprintf(stderr, "dirwalk: can't open %s\n", dir);
        return;
    }

    while ((dp = readdir(dfd)) != NULL) {
        if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
            continue;
        }

        if (strlen(dir) + strlen(dp->d_name) + 2 > sizeof(name)) {
            fprintf(stderr, "dirwalk : name %s %s too long\n", dir, dp->d_name);
        } else {
            sprintf(name, "%s/%s", dir, dp->d_name);
            (*func)(name, entry, ctx);
        }
    }
    closedir(dfd);
}

int get_file_info(basic_tchar* name, get_file_cb entry, void* ctx)
{
    struct stat stbuf;

    if (stat(name, &stbuf) == -1) {
        fprintf(stderr, "file size: open %s failed\n", name);
        return S_ERROR;
    }

    if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
        dirwalk(name, get_file_info, entry, ctx);
    } else {
        // printf("%8ld %s\n", stbuf.st_size, name);
        entry(name, ctx);
    }
    return S_SUCCESS;
}

#endif

int get_files(basic_tchar* path, get_file_cb entry, void* ctx)
{
    return get_file_info(path, entry, ctx);
}
